import {DataNode, EventDataNode} from "antd/es/tree";
import {TreeProps} from "antd";
import {Key} from "react";
import {NodeDragEventParams} from "rc-tree/lib/contextTypes";


type DropEvent = NodeDragEventParams<DataNode> & {
    dragNode: EventDataNode<DataNode>;
    dragNodesKeys: Key[];
    dropPosition: number;
    dropToGap: boolean;
}

export class DropHandle {
    even: DropEvent;
    source?: DataNode;
    sourcePos: string;
    sameParent: boolean;
    treeData: TreeProps["treeData"];
    hasMove: boolean;
    target?: DataNode;
    targetPos: string;
    parent?: DataNode;
    pos: number;

    constructor(even: DropEvent, treeData: TreeProps["treeData"]) {
        this.even = even
        this.treeData = treeData
        const {dragNode, dropPosition, dropToGap, node} = this.even
        this.source = getNode(treeData, dragNode.pos)
        this.sourcePos = dragNode.pos
        this.pos = dropToGap ? (dropPosition > 0 ? dropPosition : 0) : 0;

        const targetPosArray = node.pos.split("-");
        const parentPos = (dropToGap ? targetPosArray.splice(0, targetPosArray.length - 1) : targetPosArray).join("-")
        const targetPos = parentPos + "-" + this.pos

        this.parent = getNode(treeData, parentPos)
        this.target = getNode(treeData, targetPos)

        this.targetPos = targetPos
        this.hasMove = targetPos !== dragNode.pos
        this.sameParent = dragNode.pos.substr(0, dragNode.pos.lastIndexOf("-")) === parentPos
    }


    doMove = () => {
            if (!this.hasMove) {
                return this.treeData;
            }
            const newVal = JSON.parse(JSON.stringify(this.treeData));

            if (this.sameParent) {
                const targetArray = this.targetPos.split("-");
                const sourceArray = this.sourcePos.split("-");
                const sourceIndex = Number(sourceArray[sourceArray.length - 1])
                const targetIndex = Number(targetArray[targetArray.length - 1])
                removeNode(newVal, this.sourcePos)
                if (targetIndex < sourceIndex) {
                    setNode(newVal, this.targetPos, this.source!)
                } else {
                    targetArray[targetArray.length - 1] = String(targetIndex - 1)
                    setNode(newVal, targetArray.join("-"), this.source!)
                }
            } else {
                removeNode(newVal, this.sourcePos)
                setNode(newVal, this.targetPos, this.source!)
            }

            return newVal
    };
}

function getNode(data: TreeProps["treeData"], posStr: string) {
    posStr = posStr.substr(2);
    if (posStr === '') {
        return undefined
    }
    let posArray = posStr.split("-")
    let current: DataNode | undefined = undefined;
    for (let i in posArray) {
        const index = Number(posArray[i])
        if (Number(i) === (posArray.length - 1)) {
            return current === undefined ? data![index] : current.children![index]
        } else {
            current = current === undefined ? data![index] : current!.children![index]
        }
    }
    return current;
}

function setNode(data: TreeProps["treeData"], posStr: string, newData: DataNode) {
    posStr = posStr.substr(2);
    let posArray = posStr.split("-")
    let current: undefined | DataNode = undefined;
    for (let i in posArray) {
        const index = Number(posArray[i])
        let array: TreeProps["treeData"] = current === undefined ? data : current.children!;
        if (Number(i) === (posArray.length - 1)) {
            if (array!.length <= index) {
                array!.splice(array!.length, 0, newData)
            } else {
                array!.splice(index, 0, newData)
            }
        } else {
            current = array![index]
        }
    }
}

function removeNode(data: TreeProps["treeData"], posStr: string) {
    posStr = posStr.substr(2);
    let posArray = posStr.split("-")
    let current: DataNode | undefined = undefined;
    for (let i in posArray) {
        const index = Number(posArray[i])
        if (Number(i) === (posArray.length - 1)) {
            return current === undefined ? data!.splice(index, 1) : current.children!.splice(index, 1)
        } else {
            current = current === undefined ? data![index] : current.children![index]
        }
    }
    return current;
}
